%{
#include "expression.h"
#include <iostream>
#include <cstring>
#include <cstdlib>

TREE finalTree;

typedef struct _ATRIBUTO
{
	string nome;
        TREE tree;
        _ATRIBUTO& operator=(_ATRIBUTO& a2)
        {
            nome = a2.nome;
            tree = a2.tree;
            return *this;
        }
}ATRIBUTO;

#define YYSTYPE ATRIBUTO

template<class T>
vector<T>& operator+=(vector<T>& a, const vector<T>& b);

extern "C"
{
    int yylex();
    int yyparse();
}
void yyerror(const char *);
void valorsinal(ATRIBUTO& ss, ATRIBUTO& s1);
Operator getOperatorCode(string token);
void operacao(ATRIBUTO& ss, ATRIBUTO& s1, ATRIBUTO& s3, string op);
void final(ATRIBUTO& s1);
void consts(ATRIBUTO& ss, ATRIBUTO& s1);
Operator getFuncCode(string token);
void funcs(ATRIBUTO& ss, ATRIBUTO& s1, string func);
void var(ATRIBUTO& ss, ATRIBUTO& s1);

%}

%token TK_FUNC TK_CONST TK_VAR

%left '+' '-'
%left '*' '/'
%left '^'

%%

EI : E                                        { $$ = $1; final($1); }
   ;

E  : E '+' E                                  { operacao($$, $1, $3, "+"); }
   | E '-' E                                  { operacao($$, $1, $3, "-"); }
   | E '*' E                                  { operacao($$, $1, $3, "*"); }
   | E '/' E                                  { operacao($$, $1, $3, "/"); }
   | E '^' E                                  { operacao($$, $1, $3, "^"); }
   | F      				      { $$ = $1; }
   ;

F  : TEMP                                     { $$ = $1; } 
   | '-' TEMP                                 { valorsinal($$, $2);  }
   ;

TEMP : TK_CONST                               { consts($$,$1);         }
     | TK_VAR                                 { var($$,$1);       }
     | TK_FUNC '(' E ')'                      { funcs($$,$3, $1.nome); }
     | '(' E ')'                              { $$ = $2;               }
     ;

%%
#include "lex.yy.c"

void yyerror( const char* st )
{
  	cout << st;
}

TREE parse(std::string s)
{
	
        int len = strlen(s.c_str());
	char *temp = new char[ len + 2 ];
	strcpy( temp, s.c_str() );
	temp[ len + 1 ] = 0; // The first NULL terminator is added by strcpy
	
	YY_BUFFER_STATE my_string_buffer = yy_scan_string(temp); 
	yy_switch_to_buffer( my_string_buffer ); // switch flex to the buffer we just created
	yyparse(); 
	yy_delete_buffer(my_string_buffer );	
	
        delete [] temp;

	return finalTree;
}

template<class T>
vector<T>& operator+=(vector<T>& a, const vector<T>& b)
{
	for(int i = 0; i < b.size(); ++i)
		a.push_back(b[i]);
	return a;
}

void valorsinal(ATRIBUTO& ss, ATRIBUTO& s1)
{
        ss.tree.tree.clear();
        ss.tree.tree.push_back(SIGNALCHANGE);
        ss.tree.tree += s1.tree.tree;

        ss.tree.consts.clear();
        ss.tree.consts += s1.tree.consts;

}

Operator getOperatorCode(string token)
{
    if(token == "+")
        return ADDITION;
    if(token == "-")
        return SUBTRACTION;
    if(token == "*")
        return MULTIPLICATION;
    if(token == "/")
        return DIVISION;
    if(token == "^")
        return POTENCY;

    return CONSTANT;
}

void operacao(ATRIBUTO& ss, ATRIBUTO& s1, ATRIBUTO& s3, string op)
{
    ss.tree.tree.clear();
    ss.tree.tree.push_back(getOperatorCode(op));
    ss.tree.tree += s1.tree.tree;
    ss.tree.tree += s3.tree.tree;

    ss.tree.consts.clear();
    ss.tree.consts += s1.tree.consts;
    ss.tree.consts += s3.tree.consts;
}

void final(ATRIBUTO& s1)
{
    finalTree.tree   = s1.tree.tree;
    finalTree.consts = s1.tree.consts;
}
void consts(ATRIBUTO& ss, ATRIBUTO& s1)
{
    ss.tree.tree.clear();
    ss.tree.tree.push_back(CONSTANT);

    ss.tree.consts.clear();

    if(s1.nome == "pi")
        ss.tree.consts.push_back(3.14159265359);
    else if(s1.nome == "e")
        ss.tree.consts.push_back(2.71828182846);
    else
        ss.tree.consts.push_back((float)atof(s1.nome.c_str()));
}

Operator getFuncCode(string token)
{
    if(token == "exp")
        return EXPONENTIAL;
    if(token == "ln")
        return LN;
    if(token == "cos")
        return COSINE;
    if(token == "sin")
        return SINE;
    if(token == "tan")
        return TANGENT;


    return CONSTANT;
}

void funcs(ATRIBUTO& ss, ATRIBUTO& s1, string func)
{
    ss.tree.tree.clear();
    ss.tree.tree.push_back(getFuncCode(func));
    ss.tree.tree += s1.tree.tree;

    ss.tree.consts.clear();
    ss.tree.consts += s1.tree.consts;
}

void var(ATRIBUTO& ss, ATRIBUTO& s1)
{
    ss.tree.tree.clear();
    if(s1.nome == "x")
        ss.tree.tree.push_back(X);
    else
        ss.tree.tree.push_back(Y);

    ss.tree.consts.clear();
}

string traduzEnum(Operator o)
{
	switch(o)
    {
    case EXPONENTIAL:
	return "exp";
    case SIGNALCHANGE:
	return "--";
    case COSINE:
	return "cos";
    case SINE:
        return "sin";
    case TANGENT:
        return "tan";
    case LN:
        return "ln";
    case X:
	return "x";
    case Y:
	return "y";
    case CONSTANT:
        return "c";

    case MULTIPLICATION:
	return "*";
    case DIVISION:
	return "/";
    case SUBTRACTION:
	return "-";
    case ADDITION:
	return "+";
    case POTENCY:
        return "^";
    default:
        return "";
    }
}

void printTree(TREE& t)
{
	int cons = 0;
	for(int i = 0; i < t.tree.size(); ++i)
	{
		if(t.tree[i] == CONSTANT)
			cout << t.consts[cons++] << " ";
		else
                        cout << traduzEnum(t.tree[i]).c_str() << " ";
	}
	cout << endl;
}
